/******************************************************************************
*  COPYRIGHT MorningCore CO.,LTD
******************************************************************************/

/*****************************************************************
* FileName::    <SRP_PRACH_ParaGen.c>
* version::     <1.0.0>
* Purpose:      <calculates the prach parameter>
* Authors:      <shanshan, 2018-10-29>
* Notes:        <no>
******************************************************************/

/******************************************************************************
*  HISTORY OF CHANGES
*   <Date>          <Author>        <Version>       <DESCRIPTION>
*   2018-10-29      shanshan          V1.0.0        creat
******************************************************************************/

/******************************************************************************
*  INCLUDE FILES
******************************************************************************/
#include "SRP_PRACH_ParaGen.h"

/*----------- function definition-----------------------------------*/
/*****************************************************************************
* Function: SRP_PRACH_ParaGen()
* Purpose:  This function calculates the prach parameter
*
* Relation:
Calls       : ....
Called by   : ....
* Params:
*   <Name>          <Type>      <In/Out>    <Description>
* Return:  void
* Note:    <no>
*******************************************************************************/
void SRP_PRACH_ParaGen(stULParamTable *SimParam)
{
    // PRACH FFT length
	int IFFT_Len_WX[12] = {4096, 4096, 2048, 1024, 4096, 4096};
	int SCS_WX[12] = {15, 30, 60, 120, 15, 30};
    int BWP_Size = SimParam->SCS_RbNum[SimParam->UeScsIdx];
    int PUSCH_Scs_Index = (int)(log2(SimParam->SCS[SimParam->UeScsIdx] / 15));
    int k0 = SimParam->SCS_k0[SimParam->UeScsIdx];
	int i, k_freqoffset = 0;
	float P_PRACH = 0.0;

	// L_RA
	if (SimParam->PRACH_Format < PRACH_WX_Format4)  // format 0~3
	{
		SimParam->L_RA = 839;
		SimParam->RA_IFFT_Size = 4096;
	}
	else if (SimParam->PRACH_Format < PRACH_WX_Format8)  // format 4~7
	{
		SimParam->L_RA = 139;

		if (SimParam->PRACH_Format / 2 == 2) // format 4 and 5
		{
			SimParam->RA_IFFT_Size = (int)(2048 * pow(2, SimParam->BW_switch % 2));
		}
		else // format 6 and 7
		{
			SimParam->RA_IFFT_Size = (int)(1024 * pow(2, SimParam->BW_switch));
		}
	}
	else // format 8~11
	{
		SimParam->L_RA = 251;
		SimParam->RA_IFFT_Size = 4096;
	}

    // Cv and u
    PRACH_CS_ParaGen(SimParam);

    // get u from PRACH_RootIdx
    if (839 == SimParam->L_RA)  // 839
    {
        SimParam->PRACH_ZC_u = MapTable_839[SimParam->PRACH_RootIdx];
		SimParam->PRACH_DFT_Shift = 5;
    }
    else if(139 == SimParam->L_RA)  // 139
    {
        SimParam->PRACH_ZC_u = MapTable_139[SimParam->PRACH_RootIdx];
		SimParam->PRACH_DFT_Shift = 4;
    }
	else // 251
	{
		SimParam->PRACH_ZC_u = MapTable_251[SimParam->PRACH_RootIdx];
		SimParam->PRACH_DFT_Shift = 4;
	}
	SimParam->PRACH_RA_Shift = 3;
	SimParam->PRACH_Scale = (float)(pow(2.0, 14 - (1 + SimParam->PRACH_DFT_Shift + SimParam->PRACH_RA_Shift)));

	// N_Up
	SimParam->RA_N_up = (int)(122880 * pow(2, SimParam->BW_switch) / (SimParam->RA_IFFT_Size*SCS_WX[SimParam->PRACH_Format / 2]));

	/////////////////// Calc Nrep
	if (SimParam->PRACH_Format % 2 == 0)
	{
		SimParam->Nrep = 13;
	}
	else
	{
		SimParam->Nrep = 28;
	}
	/////////////////// Calc CP_Len
	SimParam->RA_CP_Len = SimParam->RA_IFFT_Size;

	// update scaling_sch
	SimParam->sum_shift = 1;
	for (i = 0; i < 12; i++)
	{
		SimParam->ifft_shift_factor[i] = StageRightShift_SmallRB[(int)(log2(SimParam->RA_IFFT_Size)) - 7][i];
		SimParam->sum_shift = SimParam->sum_shift + SimParam->ifft_shift_factor[i];
	};

	// IFFT_Len*N_rep+Ncp
	SimParam->RA_IFFT_Op_Len = SimParam->RA_IFFT_Size * SimParam->Nrep + SimParam->RA_CP_Len;
    // FilterOut length
    SimParam->RA_Filter_Op_Len = SimParam->RA_IFFT_Op_Len * SimParam->RA_N_up;

	SimParam->K_ratio = SimParam->SCS[SimParam->UeScsIdx] / SCS_WX[SimParam->PRACH_Format / 2];

	// RA RB
    if (SimParam->L_RA == 839) // 839
    {
        SimParam->N_RA_PRB = (int)((SimParam->L_RA + 25) / 12.0 / SimParam->K_ratio); // RB number in PUSCH
    }
    else if (SimParam->L_RA == 139) //139
    {
        SimParam->N_RA_PRB = (int)((SimParam->L_RA + 5) / 12.0 / SimParam->K_ratio); // RB number in PUSCH
    }
	else // 251
	{
		SimParam->N_RA_PRB = (int)((SimParam->L_RA + 37) / 12.0 / SimParam->K_ratio); // RB number in PUSCH
	}

	if (SimParam->L_RA == 839) // 839
	{
		k_freqoffset = 12;
	}
	else if (SimParam->L_RA == 139) //139
	{
		k_freqoffset = 2;
	}
	else // 251
	{
		k_freqoffset = 18;
	}

	SimParam->k_freqoffset = k_freqoffset + (int)((SimParam->RA_IFFT_Size - SimParam->K_ratio*SimParam->N_RA_PRB * 12) / 2);

    ////////////
	SimParam->k2_shift = (int)(SimParam->RA_IFFT_Size / 2);
	SimParam->RA_F_o = (k0 + SimParam->N_RA_PRB * SC_PER_RB / 2 + SimParam->n_PRACH_PRB * SC_PER_RB - BWP_Size * SC_PER_RB / 2);

    ////////////////// Select filter coef
    switch (SimParam->RA_N_up)
    {
        case 1:
            memset(SimParam->RA_Filter_Coef, 0, 49 * sizeof(double));
		case 2:
			memcpy(SimParam->RA_Filter_Coef, h_Tx_Up2, 49 * sizeof(double));
			break;
        case 4:
            memcpy(SimParam->RA_Filter_Coef, h_Tx_Up4, 49 * sizeof(double));
            break;
        case 6:
            memcpy(SimParam->RA_Filter_Coef, h_Tx_Up6, 49 * sizeof(double));
            break;
        case 8:
            memcpy(SimParam->RA_Filter_Coef, h_Tx_Up8, 49 * sizeof(double));
            break;
        default:
            memset(SimParam->RA_Filter_Coef, 0, 49 * sizeof(double));
            printf("ERROR!! Invalid UpSample Factor!\n");
            throw(0);
    }

	// beta
	P_PRACH = (float)(pow(SimParam->L_RA / pow(2.0, SimParam->PRACH_RA_Shift + SimParam->PRACH_DFT_Shift + SimParam->sum_shift), 2));
	SRP_para_cal(SimParam->P_target, P_PRACH, &SimParam->RA_Lamda_Scale, &SimParam->N_Scale);
}

/*****************************************************************************
* Function: PRACH_CS_ParaGen()
* Purpose:  This function performs ZC root sequence cyclic shift calculation
*
* Relation:
            Calls       : None
            Called by   : SRP_PRACH_ParaGen()
* Params:
*   <Name>                  <Type>          <In/Out>    <Description>
*   SimParam                stULParamTable   In          param
* Return:  void
* Note:    None
*******************************************************************************/
void PRACH_CS_ParaGen(stULParamTable *SimParam)
{
    uint16 N_cs_Table_1p25k[16][3] =
    {
        {0, 15,   15},      // 0
        {13, 18,  18},    // 1
        {15, 22,  22},    // 2
        {18, 26,  26},    // 3
        {22, 32,  32},    // 4
        {26, 38,  38},    // 5
        {32, 46,  46},    // 6
        {38, 55,  55},    // 7
        {46, 68,  68},    // 8
        {59, 82,  82},    // 9
        {76, 100, 100},   // 10
        {93, 128, 118},   // 11
        {119, 158, 137},  // 12
        {167, 202, 0},    // 13
        {279, 237, 0},    // 14
        {419, 0, 0  }    // 15
    };
    uint16 N_cs_Table_5k[16][3] =
    {
        {0,  36,    36}, // 0
        {13,  57,   57}, // 1
        {26,  72,   60}, // 2
        {33,  81,   63}, // 3
        {38,  89,   65}, // 4
        {41,  94,   68}, // 5
        {49,  103,  71}, // 6
        {55,  112,  77}, // 7
        {64,  121,  81}, // 8
        {76,  132,  85}, // 9
        {93,  137,  97}, // 10
        {119,  152, 109},// 11
        {139,  173, 122},// 12
        {209,  195, 137},// 13
        {279,  216, 0  },// 14
        {419,  237, 0  } // 15
    };
    uint16 N_cs_Table_139[16] = {0, 2, 4, 6, 8, 10, 12, 13, 15, 17, 19, 23, 27, 34, 46, 69};     // 0~15
	uint16 N_cs_Table_251[16] = {0, 13, 26, 39, 52, 79, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };     // 0~15
    uint16 Ncs = 0;
    uint16 q_RA = 0; // q_RA*u%L_RA=1
    uint16 du_RA = 0;
    uint16  N_Cv, N_Logic, ZC_v = 0;
    uint16 n_shift_RA, n_group_RA, n_shift_RA1, n_shift_RA2, n_shift_RA3 = 0;
    uint16 temp, d_start, d_start2, d_start3 = 0;
    //int32   Flag = 0;
    //char   cFileAddr[256] = {0};

    //sprintf_s(cFileAddr,"Result/PRACHTestData/Cv_u_test.am",1);

    // get u from PRACH_RootIdx
    if (839 == SimParam->L_RA)  // 839
    {
        SimParam->PRACH_ZC_u = MapTable_839[SimParam->PRACH_RootIdx];
    }
    else if(139 == SimParam->L_RA)  // 139
    {
        SimParam->PRACH_ZC_u = MapTable_139[SimParam->PRACH_RootIdx];
    }
	else // 251
	{
		SimParam->PRACH_ZC_u = MapTable_251[SimParam->PRACH_RootIdx];
	}

    // get Ncs from ZeroCorrelationConfig and PRACH_HighSpeedFlag
	if (839 == SimParam->L_RA)  // 839
	{
		Ncs = N_cs_Table_1p25k[SimParam->Ncs_Config][SimParam->PRACH_HighSpeedFlag];
	}
	else if (139 == SimParam->L_RA)  // 139
	{
		Ncs = N_cs_Table_139[SimParam->Ncs_Config];
	}
	else // 251
	{
		Ncs = N_cs_Table_251[SimParam->Ncs_Config];
	}


    // get q_RA which is the smallest non-negative integer that fulfils q_RA*u%L_RA = 1
    q_RA = InverseCal(SimParam->L_RA, SimParam->PRACH_ZC_u);

    // du_RA in 38.211 6.3.3.1
    if ((q_RA >= 0) & (q_RA < SimParam->L_RA / 2))
    {
        du_RA = q_RA;
    }
    else
    {
        du_RA = SimParam->L_RA - q_RA;
    }

    // Cv Calculation
    if (0 == SimParam->PRACH_HighSpeedFlag) // unrestricted
    {
        // 64: first Cv order, second Logical index order
        if (0 == Ncs)
        {
            N_Cv = 1;
            SimParam->PRACH_ZC_Cv = 0;
            //Flag = 1;
            //vFilePrintDec(cFileAddr, &Flag, 1);
        }
        else
        {
            N_Cv = SimParam->L_RA / Ncs; // number of Cv in one logical index
            ZC_v = SimParam->PRACH_PreambleSeq % N_Cv;
            SimParam->PRACH_ZC_Cv = ZC_v * Ncs;
            //Flag = 2;
            //vFilePrintDec(cFileAddr, &Flag, 1);
        }

        N_Logic = SimParam->PRACH_PreambleSeq / N_Cv;   // number of logical index
        SimParam->PRACH_RootIdx = (SimParam->PRACH_RootIdx + N_Logic) % (SimParam->L_RA - 1);
    } // end of // unrestricted
    else if (1 == SimParam->PRACH_HighSpeedFlag) // restricted type A
    {
        if ((du_RA < Ncs) | (du_RA > (SimParam->L_RA - Ncs) / 2)) // no cyclic shift, PRACH_ZC_u is not changed
        {
            SimParam->PRACH_ZC_Cv = 0;
            SimParam->PRACH_RootIdx = (SimParam->PRACH_RootIdx + 1) % (SimParam->L_RA - 1);
            //Flag = 3;
            //vFilePrintDec(cFileAddr, &Flag, 1);
            PRACH_CS_ParaGen(SimParam);
        }
        else  // cyclic shift, PRACH_ZC_u is changed, set1
        {
            if ((du_RA >= Ncs) & (du_RA < SimParam->L_RA / 3))
            {
                n_shift_RA = du_RA / Ncs;
                d_start = 2 * du_RA + n_shift_RA * Ncs;
                n_group_RA = SimParam->L_RA / d_start;
                n_shift_RA1 = max((SimParam->L_RA - 2 * du_RA - n_group_RA * d_start) / Ncs, 0);
                //Flag = 4;
                //vFilePrintDec(cFileAddr, &Flag, 1);
            }
            else
            {
                n_shift_RA = (SimParam->L_RA - 2 * du_RA) / Ncs;
                d_start = SimParam->L_RA - 2 * du_RA + n_shift_RA * Ncs;
                n_group_RA = du_RA / d_start;
                temp = max((du_RA - n_group_RA * d_start) / Ncs, 0);
                n_shift_RA1 = min(temp, n_shift_RA);
                //Flag = 5;
                //vFilePrintDec(cFileAddr, &Flag, 1);
            }

            N_Cv = n_shift_RA * n_group_RA + n_shift_RA1; // number of Cv in one logical index

            if (SimParam->PRACH_PreambleSeq >= N_Cv)
            {
                SimParam->PRACH_PreambleSeq = SimParam->PRACH_PreambleSeq - N_Cv;
                SimParam->PRACH_RootIdx = (SimParam->PRACH_RootIdx + 1) % (SimParam->L_RA - 1); // next u
                //Flag = 6;
                //vFilePrintDec(cFileAddr, &Flag, 1);
                PRACH_CS_ParaGen(SimParam);
            }
            else
            {
                SimParam->PRACH_ZC_Cv = d_start * (SimParam->PRACH_PreambleSeq / n_shift_RA) + (SimParam->PRACH_PreambleSeq % n_shift_RA) * Ncs;
                //Flag = 7;
                //vFilePrintDec(cFileAddr, &Flag, 1);
            }
        }
    } // end of restricted type A, set1
    else // restricted type B
    {
        if ((du_RA < Ncs) |
                ((du_RA > (SimParam->L_RA - Ncs) / 4) & (du_RA < (SimParam->L_RA + Ncs) / 4)) |
                ((du_RA > (SimParam->L_RA - Ncs) / 3) & (du_RA < (SimParam->L_RA + Ncs) / 3)) |
                (du_RA > (SimParam->L_RA - Ncs) / 2)) // no cyclic shift, PRACH_ZC_u is not changed
        {
            SimParam->PRACH_ZC_Cv = 0;
            SimParam->PRACH_RootIdx = (SimParam->PRACH_RootIdx + 1) % (SimParam->L_RA - 1);
            //Flag = 8;
            //vFilePrintDec(cFileAddr, &Flag, 1);
            PRACH_CS_ParaGen(SimParam);
        }
        else  // cyclic shift, PRACH_ZC_u is changed
        {
            if ((du_RA >= Ncs) & (du_RA <= (SimParam->L_RA - Ncs) / 4)) // set 2
            {
                if (du_RA < SimParam->L_RA / 5)
                {
                    n_shift_RA = du_RA / Ncs;
                    d_start = 4 * du_RA + n_shift_RA * Ncs;
                    n_group_RA = SimParam->L_RA / d_start;
                    n_shift_RA1 = max((SimParam->L_RA - 4 * du_RA - n_group_RA * d_start) / Ncs, 0);
                    //Flag = 9;
                    //vFilePrintDec(cFileAddr, &Flag, 1);
                }
                else
                {
                    n_shift_RA = (SimParam->L_RA - 4 * du_RA) / Ncs;
                    d_start = SimParam->L_RA - 4 * du_RA + n_shift_RA * Ncs;
                    n_group_RA = du_RA / d_start;
                    temp = max((du_RA - n_group_RA * d_start) / Ncs, 0);
                    n_shift_RA1 = min(temp, n_shift_RA);
                    //Flag = 10;
                    //vFilePrintDec(cFileAddr, &Flag, 1);
                }

                N_Cv = n_shift_RA * n_group_RA + n_shift_RA1; // number of Cv in one logical index

                if (SimParam->PRACH_PreambleSeq >= N_Cv)
                {
                    SimParam->PRACH_PreambleSeq = SimParam->PRACH_PreambleSeq - N_Cv;
                    SimParam->PRACH_RootIdx = (SimParam->PRACH_RootIdx + 1) % (SimParam->L_RA - 1); // next u
                    //Flag = 11;
                    //vFilePrintDec(cFileAddr, &Flag, 1);
                    PRACH_CS_ParaGen(SimParam);
                }
                else
                {
                    SimParam->PRACH_ZC_Cv = d_start * (SimParam->PRACH_PreambleSeq / n_shift_RA) + (SimParam->PRACH_PreambleSeq % n_shift_RA) * Ncs;
                    //Flag = 12;
                    //vFilePrintDec(cFileAddr, &Flag, 1);
                }
            } // end of if ((du_RA >= Ncs) & (du_RA < (SimParam->L_RA - Ncs) / 4))  // set 2
            else    // set3
            {
                if ((du_RA >= (SimParam->L_RA + Ncs) / 4) & (du_RA < 2 * SimParam->L_RA / 7))   // set 3
                {
                    n_shift_RA = (4 * du_RA - SimParam->L_RA) / Ncs;
                    d_start = 4 * du_RA - SimParam->L_RA + n_shift_RA * Ncs;
                    n_group_RA = du_RA / d_start;

                    n_shift_RA1 = max((SimParam->L_RA - 3 * du_RA - n_group_RA * d_start) / Ncs, 0);
                    temp = min(du_RA - n_group_RA * d_start, 4 * du_RA - SimParam->L_RA - n_shift_RA1 * Ncs);
                    n_shift_RA2 = temp / Ncs;
                    temp = n_shift_RA1 > 1 ? 1 : n_shift_RA1; // min(1, n_shift_RA1)
                    n_shift_RA3 = ((1 - temp) * (du_RA - n_group_RA * d_start) + temp * (4 * du_RA - SimParam->L_RA - n_shift_RA1 * Ncs)) / Ncs - n_shift_RA2;

                    d_start2 = SimParam->L_RA - 3 * du_RA + n_group_RA * d_start + n_shift_RA1 * Ncs;
                    d_start3 = SimParam->L_RA - 2 * du_RA + n_group_RA * d_start + n_shift_RA2 * Ncs;
                    //Flag = 13;
                    //vFilePrintDec(cFileAddr, &Flag, 1);
                }
                else if ((du_RA >= 2 * SimParam->L_RA / 7) & (du_RA <= (SimParam->L_RA - Ncs) / 3))
                {
                    n_shift_RA = (SimParam->L_RA - 3 * du_RA) / Ncs;
                    d_start = SimParam->L_RA - 3 * du_RA + n_shift_RA * Ncs;
                    n_group_RA = du_RA / d_start;

                    n_shift_RA1 = max((4 * du_RA - SimParam->L_RA - n_group_RA * d_start) / Ncs, 0);
                    temp = min(du_RA - n_group_RA * d_start, SimParam->L_RA - 3 * du_RA - n_shift_RA1 * Ncs);
                    n_shift_RA2 = temp / Ncs;
                    n_shift_RA3 = 0;

                    d_start2 = du_RA + n_group_RA * d_start + n_shift_RA1 * Ncs;
                    d_start3 = 0;
                    //Flag = 14;
                    //vFilePrintDec(cFileAddr, &Flag, 1);
                }
                else if ((du_RA >= (SimParam->L_RA + Ncs) / 3) & (du_RA < 2 * SimParam->L_RA / 5))
                {
                    n_shift_RA = (3 * du_RA - SimParam->L_RA) / Ncs;
                    d_start = 3 * du_RA - SimParam->L_RA + n_shift_RA * Ncs;
                    n_group_RA = du_RA / d_start;

                    n_shift_RA1 = max((SimParam->L_RA - 2 * du_RA - n_group_RA * d_start) / Ncs, 0);
                    n_shift_RA2 = 0;
                    n_shift_RA3 = 0;

                    d_start2 = 0;
                    d_start3 = 0;
                    //Flag = 15;
                    //vFilePrintDec(cFileAddr, &Flag, 1);
                }
                else
                {
                    n_shift_RA = (SimParam->L_RA - 2 * du_RA) / Ncs;
                    d_start = 2 * (SimParam->L_RA - 2 * du_RA) + n_shift_RA * Ncs;
                    n_group_RA = (SimParam->L_RA - du_RA) / d_start;

                    n_shift_RA1 = max((3 * du_RA - SimParam->L_RA - n_group_RA * d_start) / Ncs, 0);
                    n_shift_RA2 = 0;
                    n_shift_RA3 = 0;

                    d_start2 = 0;
                    d_start3 = 0;
                    //Flag = 16;
                    //vFilePrintDec(cFileAddr, &Flag, 1);
                }

                N_Cv = n_shift_RA * n_group_RA + n_shift_RA1; // number of Cv in one logical index

                if (SimParam->PRACH_PreambleSeq >= (N_Cv + n_shift_RA2 + n_shift_RA3))
                {
                    SimParam->PRACH_PreambleSeq = SimParam->PRACH_PreambleSeq - (N_Cv + n_shift_RA2 + n_shift_RA3);
                    SimParam->PRACH_RootIdx = (SimParam->PRACH_RootIdx + 1) % (SimParam->L_RA - 1); // next u
                    //Flag = 17;
                    //vFilePrintDec(cFileAddr, &Flag, 1);
                    PRACH_CS_ParaGen(SimParam);
                }
                else
                {
                    if (SimParam->PRACH_PreambleSeq >= (N_Cv + n_shift_RA2)) // N_Cv + n_shift_RA2 ~ N_Cv + n_shift_RA2 + n_shift_RA3 - 1
                    {
                        SimParam->PRACH_ZC_Cv = d_start3 + (SimParam->PRACH_PreambleSeq - N_Cv - n_shift_RA2) * Ncs;
                        //Flag = 18;
                        //vFilePrintDec(cFileAddr, &Flag, 1);
                    }
                    else if (SimParam->PRACH_PreambleSeq >= N_Cv)  // N_Cv ~ N_Cv + n_shift_RA2 - 1
                    {
                        SimParam->PRACH_ZC_Cv = d_start2 + (SimParam->PRACH_PreambleSeq - N_Cv) * Ncs;
                        //Flag = 19;
                        //vFilePrintDec(cFileAddr, &Flag, 1);
                    }
                    else    // 0 ~ N_Cv - 1
                    {
                        SimParam->PRACH_ZC_Cv = d_start * (SimParam->PRACH_PreambleSeq / n_shift_RA) + (SimParam->PRACH_PreambleSeq % n_shift_RA) * Ncs;
                        //Flag = 20;
                        //vFilePrintDec(cFileAddr, &Flag, 1);
                    }
                }
            } // set3
        } // end of no cyclic shift, PRACH_ZC_u is not changed
    }// end of restricted type B
}
